package com.sparrow.rpc.util;

import com.sparrow.rpc.domain.RpcNode;
import com.sparrow.rpc.service.RpcService;
import com.sparrow.rpc.future.RpcFuture;
import com.sparrow.rpc.serialize.impl.KryoSerializer;
import com.sparrow.rpc.serialize.Serializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;

/**
 * RpcUtil 类提供了一系列与 RPC 相关的实用方法，包括序列化器管理、方法键生成、错误信息获取、UUID 生成和返回类型检查等。
 */
public class RpcUtil {

    private static Logger logger = LoggerFactory.getLogger(RpcUtil.class);

    // 默认的序列化器，使用 ProtostuffSerializer 实现
    public static final Serializer SERIALIZER = new KryoSerializer();
    private static final ThreadLocal<KryoSerializer> kryos = new ThreadLocal<KryoSerializer>() {
        @Override
        protected KryoSerializer initialValue() {
            return new KryoSerializer();
        }
    };

    // 用于缓存方法的唯一键，避免重复生成
    private static final ConcurrentHashMap<Method, String> methodKeyCache = new ConcurrentHashMap<>();

    // 原子性的 UUID 生成器，确保生成的 UUID 唯一
    private static final AtomicLong uuidGenerator = new AtomicLong(1);

    /**
     * 根据给定的方法生成一个唯一的键。
     * 键的格式为：类名.方法名(参数类型列表)
     *
     * @param method 要生成键的方法
     * @return 方法的唯一键
     */
    public static String getMethodKey(Method method) {
        return methodKeyCache.computeIfAbsent(method, k -> generateMethodKey(method));
    }

    private static String generateMethodKey(Method method) {
        StringBuilder methodKeyBuilder = new StringBuilder();
        // 拼接类名和方法名
        methodKeyBuilder.append(method.getDeclaringClass().getName())
                .append(".")
                .append(method.getName());
        methodKeyBuilder.append("(");
        // 获取方法的参数类型数组
        Class<?>[] parameterTypes = method.getParameterTypes();
        for (int i = 0; i < parameterTypes.length; i++) {
            Class<?> parameterType = parameterTypes[i];
            if (i == parameterTypes.length - 1) {
                methodKeyBuilder.append(parameterType.getName());
            } else {
                methodKeyBuilder.append(parameterType.getName()).append(",");
            }
        }
        methodKeyBuilder.append(")");
        return methodKeyBuilder.toString();
    }

    /**
     * 获取 Throwable 对象的详细错误信息。
     *
     * @param throwable 要获取错误信息的 Throwable 对象
     * @return 包含错误信息的字符串
     */
    public static String getErrorInfo(Throwable throwable) {
        StringWriter stringWriter = new StringWriter();
        try (PrintWriter printWriter = new PrintWriter(stringWriter)) {
            throwable.printStackTrace(printWriter);
            return stringWriter.toString();
        }
    }

    /**
     * 生成一个唯一的 UUID。
     * 当 UUID 达到最大值时，会重置为 0。
     *
     * @return 唯一的 UUID
     */
    public static long nextUUID() {
        long currentId;
        do {
            currentId = uuidGenerator.get();
            if (currentId == Long.MAX_VALUE) {
                uuidGenerator.compareAndSet(Long.MAX_VALUE, 0);
            }
        } while (!uuidGenerator.compareAndSet(currentId, currentId + 1));
        return currentId;
    }

    /**
     * 检查给定的返回类型是否为 void 类型。
     *
     * @param returnType 要检查的返回类型
     * @return 如果是 void 类型返回 true，否则返回 false
     */
    public static boolean isVoidMethod(Class<?> returnType) {
        return Void.class.isAssignableFrom(returnType) || Void.TYPE.equals(returnType);
    }

    /**
     * 检查 RPC 方法的返回类型是否符合要求。
     * 返回类型必须是 void 或者 RpcFuture 类型，否则抛出异常。
     *
     * @param method 要检查的方法
     * @return 如果返回类型符合要求返回 true
     * @throws IllegalStateException 如果返回类型不符合要求
     */
    public static boolean checkRpcReturnType(Method method) {
        Class<?> returnType = method.getReturnType();
        if (isVoidMethod(returnType)) {
            return true;
        }
        if (RpcFuture.class.isAssignableFrom(returnType)) {
            return true;
        }
        throw new IllegalStateException(method.getName() + " rpc返回类型必须是RpcFuture类型！");
    }

    /**
     * 执行异步rpc方法
     * @param nodeId 目标节点Id
     * @param clazz rpc接口
     * @param proxyConsumer  rpc代理消费逻辑
     * @param <T>
     */
    public static <T> void runAsyncRpcMethod(String nodeId, Class<T> clazz, Consumer<T> proxyConsumer) {
        RpcNode node = RpcService.getInstance().getNode(nodeId);
        if (node == null) {
            logger.error("runAsyncRpcMethod fail rpc node is null nodeId:{}", nodeId);
            return;
        }
        T proxy = RpcService.getInstance().getProxy(nodeId, clazz);
        if (proxy == null) {
            logger.error("runAsyncRpcMethod fail client is null nodeId:{}", nodeId);
            return;
        }
        proxyConsumer.accept(proxy);
    }

    public static Serializer getSerializer() {
        return kryos.get();
    }


}